/*
 * From Codeplex project SPWebServiceDeploy http://codeplex.com/SPWebServiceDeploy 
 * 
 * Copyright (c) 2007 Leon Land Gersing | fallenrogue.com
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 *
*/
using System;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace LLG.SharePoint.Services
{
    public class SPWebServiceDeployer
    {
        private readonly DirectoryInfo isapi;
        private readonly DirectoryInfo layouts;
        private readonly DirectoryInfo features;

        private const string discoLocation = @"%programfiles%\Microsoft SDKs\Windows\v6.0A\Bin";

        public DirectoryInfo hive12 { get; set; }
        public string featureName { get; set; }

        private string siteUrl;
        /// <summary>
        /// Root URL
        /// </summary>
        /// <remarks>Don't worry about anything else. Just get this url right and the world will be beautiful.</remarks>
        public string SiteUrl
        {
            get { return siteUrl; }
            set { siteUrl = value.Trim('/'); }
        }	

        public SPWebServiceDeployer(string SiteUrl, string FeatureName, string pathTo12Hive)
        {
            featureName = FeatureName;
            this.SiteUrl = SiteUrl;
            hive12 = new DirectoryInfo(pathTo12Hive);
            isapi = hive12.GetDirectories("ISAPI")[0];
            layouts = hive12.GetDirectories("TEMPLATE")[0].GetDirectories("LAYOUTS")[0];
            features = hive12.GetDirectories("TEMPLATE")[0].GetDirectories("FEATURES")[0];
        }

        public void GenWebService()
        {
            installServicesToLayout();
            runDiscoToGenFiles();
            renameAndMoveFiles();
        }

        private void installServicesToLayout()
        {
            try
            {
                foreach (FileInfo file in features.GetDirectories(featureName)[0].GetFiles("*.asmx", SearchOption.AllDirectories))
                    File.Copy(file.FullName, layouts.FullName + "\\" + file.Name, true);
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
            
        }

        public void RemoveAllWebServices()
        {
            try
            {
                foreach (FileInfo file in features.GetDirectories(featureName)[0].GetFiles("*.asmx", SearchOption.AllDirectories))
                RemoveWebService(file.Name);
            }
            catch(Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
        }

        public void RemoveWebService(string asmxFileName)
        {
            try
            {
                foreach (FileInfo web in isapi.GetFiles(asmxFileName))
                    web.Delete();
                foreach (FileInfo disco in isapi.GetFiles(asmxFileName.Replace(".asmx", "disco.aspx")))
                    disco.Delete();
                foreach (FileInfo wsdl in isapi.GetFiles(asmxFileName.Replace(".asmx", "wsdl.aspx")))
                    wsdl.Delete();
                delistWebService(asmxFileName);
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
        }

        private void listWebService(string name)
        {
            try
            {
                string contents = File.ReadAllText(isapi.FullName + "\\spdisco.aspx");
                string newService =
                    "<contractRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name +
                    "?wsdl\"), Response.Output); %> docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name +
                    "\"), Response.Output); %> xmlns=\"http://schemas.xmlsoap.org/disco/scl/\" /><discoveryRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name + "?disco\"),Response.Output); %> xmlns=\"http://schemas.xmlsoap.org/disco/\" /></discovery>";
                File.WriteAllText(isapi.FullName + "\\spdisco.aspx", Regex.Replace(contents, "</discovery>", newService));
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
        }

        private void delistWebService(string name)
        {
            try
            {
                string contents = File.ReadAllText(isapi.FullName + "\\spdisco.aspx");
                string newService =
                    "<contractRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name +
                    "?wsdl\"), Response.Output); %> docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name +
                    "\"), Response.Output); %> xmlns=\"http://schemas.xmlsoap.org/disco/scl/\" /><discoveryRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + \"/_vti_bin/" +
                    name + "?disco\"),Response.Output); %> xmlns=\"http://schemas.xmlsoap.org/disco/\" />";
                File.WriteAllText(isapi.FullName + "\\spdisco.aspx", Regex.Replace(contents, newService, ""));
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
        }

        private void renameAndMoveFiles()
        {
            try
            {
                foreach (FileInfo file in layouts.GetFiles())
                {
                    if (file.Name.EndsWith("disco"))
                    {
                        string newName = file.Name.Replace(".disco", "disco.aspx");
                        string newContents = alterDiscoContents(File.ReadAllText(file.FullName));

                        File.WriteAllText(isapi.FullName + "\\" + newName, newContents); //writes and moves it.

                        file.Delete();
                    }
                    if (file.Name.EndsWith("wsdl"))
                    {
                        string newName = file.Name.Replace(".wsdl", "wsdl.aspx");
                        string newContents = alterWsdlContents(File.ReadAllText(file.FullName));
                        File.WriteAllText(isapi.FullName + "\\" + newName, newContents);

                        file.Delete();
                    }
                    if (file.Name.EndsWith("asmx"))
                    {
                        File.WriteAllText(isapi.FullName + "\\" + file.Name, File.ReadAllText(file.FullName));
                        listWebService(file.Name);

                        file.Delete();
                    }
                    if (file.Name.Equals("results.discomap"))
                    {
                        file.Delete();
                    }
                }
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
            }
        }

        private static string alterWsdlContents(string p)
        {
            try
            {
                p = p.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>",
                              "<%@ Page Language=\"C#\" Inherits=\"System.Web.UI.Page\" %><%@ Assembly Name=\"Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c\" %><%@ Import Namespace=\"Microsoft.SharePoint.Utilities\" %><%@ Import Namespace=\"Microsoft.SharePoint\" %><% Response.ContentType = \"text/xml\"; %>");

                const string loc = "soap:address location=\\\"[^\\\"]*\\\" ";

                p = Regex.Replace(p, loc,
                                  "soap:address location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>");
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
                throw;
            }
            return p;
        }

        private static string alterDiscoContents(string p)
        {
            try
            {
                p = p.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>",
                              "<%@ Page Language=\"C#\" Inherits=\"System.Web.UI.Page\" %><%@ Assembly Name=\"Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c\" %><%@ Import Namespace=\"Microsoft.SharePoint.Utilities\" %><%@ Import Namespace=\"Microsoft.SharePoint\" %><% Response.ContentType = \"text/xml\"; %>");

                const string addr = " address=\\\"[^\\\"]*\\\" ";
                const string docRef = " docRef=\\\"[^\\\"]*\\\" ";
                const string refFer = " ref=\\\"[^\\\"]*\\\" ";

                p = Regex.Replace(p, addr,
                                  " address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>");
                p = Regex.Replace(p, docRef,
                                  " docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>");
                p = Regex.Replace(p, refFer,
                                  " ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + \"?wsdl\"),Response.Output); %>");
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("SPWebServiceDeploy", ex.ToString(), EventLogEntryType.Error);
                throw;
            }
            return p;
        }

        private void runDiscoToGenFiles()
        {
            foreach (FileInfo file in layouts.GetFiles("*.asmx"))
            {
                using (Process proc = new Process())
                {
                    proc.StartInfo.WorkingDirectory = layouts.FullName;
                    proc.StartInfo.Arguments = string.Concat(SiteUrl, "/_layouts/", file.Name);
                    proc.StartInfo.FileName = discoLocation+@"\disco.exe";
                    proc.StartInfo.RedirectStandardError = true;
                    proc.StartInfo.RedirectStandardOutput = true;
                    proc.StartInfo.UseShellExecute = false;
                    proc.StartInfo.CreateNoWindow = true;
                    proc.OutputDataReceived += proc_OutputDataReceived;
                    proc.ErrorDataReceived += proc_ErrorDataReceived;
                    proc.Start();
                    proc.BeginErrorReadLine();
                    proc.BeginOutputReadLine();
                    
                    proc.WaitForExit();
                    proc.Close();
                }
            }
        }

        static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            using (EventLog el = new EventLog())
            {
                el.Source = "WebService Deployment Class Instance";
                el.WriteEntry(e.Data,EventLogEntryType.Error);
            }
        }

        static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            using (EventLog el = new EventLog())
            {
                el.Source = "WebService Deployment Class Instance";
                el.WriteEntry(e.Data, EventLogEntryType.Information);
            }
        }
    }
}
